package com.augurit.common.common.http.kt.api

import com.augurit.common.common.http.kt.model.ApiException
import com.augurit.common.common.http.kt.model.Result
import com.augurit.common.common.http.kt.request.Request
import com.google.gson.Gson
import java.lang.reflect.Type

/**
 *  author : yanghaozhang
 *  date : 2022/5/4 23:19
 *  description :
 */
typealias IApiRequest<Response> = suspend () -> Response

interface RequestConvertible<T> {
    val request: Request<T>
}

interface IApiTransform<Response, OutputData> {
    suspend fun transform(bean: Response): OutputData
}

class ApiTransformImpl<Response, OutputData>(private val type: Type) : IApiTransform<Response, Result<OutputData, Exception>> {
    override suspend fun transform(bean: Response): Result<OutputData, Exception> {
        return when (bean) {
            is String -> {
                val result = Gson().fromJson<OutputData>(bean, type)
                return Result.success(result)
            }
            else -> {
                Result.success(bean as OutputData)
            }
        }
    }
}

interface IApiTask<Response, OutputData> {

    val request: RequestConvertible<Response>

    val apiTransform: IApiTransform<Response, OutputData>

    suspend fun doBeforeRequest()

    suspend fun doRequest(): OutputData

    suspend fun doAfterRequest(outputData: OutputData)

    suspend fun handleException(throwable: Throwable): OutputData
}

abstract class BaseApiTask<Response, OutputData> : IApiTask<Response, OutputData> {

    override suspend fun doRequest(): OutputData {
        val apiRequest = request.request.apiRequest
        doBeforeRequest()
        val result = apiRequest.invoke()
        val outputData = apiTransform.transform(result)
        doAfterRequest(outputData)
        return outputData
    }
}

class DirectApiImpl<Response, OutputData>(
    override val request: RequestConvertible<Response>,
    override val apiTransform: IApiTransform<Response, Result<OutputData, Exception>>
) : BaseApiTask<Response, Result<OutputData, Exception>>() {

    override suspend fun doRequest(): Result<OutputData, Exception> {
        val outputData: Result<OutputData, Exception> = try {
            super.doRequest()
        } catch (e: Exception) {
            e.printStackTrace()
            handleException(e)
        }
        return outputData
    }

    override suspend fun doBeforeRequest() {

    }

    override suspend fun doAfterRequest(outputData: Result<OutputData, Exception>) {

    }

    override suspend fun handleException(throwable: Throwable): Result<OutputData, Exception> {
        return Result.error(ApiException.handleException(throwable))
    }
}